package org.jacp.demo.util;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import javax.swing.ImageIcon;

import org.im4java.core.ConvertCmd;
import org.im4java.core.IM4JavaException;
import org.im4java.core.IMOperation;
import org.im4java.process.ProcessEvent;
import org.im4java.process.ProcessEventListener;
import org.im4java.process.ProcessExecutor;
import org.im4java.process.ProcessStarter;
import org.im4java.process.ProcessTask;
import org.jdesktop.swingx.graphics.GraphicsUtilities;

import com.sun.image.codec.jpeg.ImageFormatException;

public class LoadReducedImagePerformanceTest {

	private static final org.jacp.demo.util.LoadReducedImagePerformanceTest.ProccessTerminationListener listener = new org.jacp.demo.util.LoadReducedImagePerformanceTest.ProccessTerminationListener();
	private static ProcessExecutor exec = null;
	static {
		Runtime runtime = Runtime.getRuntime();
		int nrOfProcessors = runtime.availableProcessors();
		exec = new ProcessExecutor(nrOfProcessors+1);
	}

	/**
	 * @param args
	 */
	public static void main(final String[] args) {
		/*
		 * loadJAI(); loadStandard(); loadByToolkit(); loadJAI();
		 * loadStandard();
		 */
		// ProcessStarter.setGlobalSearchPath("/opt/local/bin/");
		loadByCMDTHREAD();
		// loadByToolkit();
		// loadStandard();
		// loadStandard();
	}

	private static void loadByCMDTHREAD() {
		final long time = -System.currentTimeMillis();
		final File dir = new File("/home/amo/Bilder/Teamevent_Andy/104_PANA/");

		// create the operation, add images and operators/options
		final IMOperation op = new IMOperation();
		// op.resize(88);
		op.size(160);
		op.addImage();
		op.thumbnail(160);
		op.addImage(); // placeholder output filename

		if (dir.isDirectory()) {
			final File[] files = dir.listFiles();
			int i = 0;
			final ConvertCmd cmd = new ConvertCmd();
			cmd.setAsyncMode(true);
			cmd.addProcessEventListener(listener);
			for (final File f : files) {
				final long time1 = -System.currentTimeMillis();
				String file = f.getAbsolutePath();
				if (file.toLowerCase().contains(".jpg")) {
					final int lastDot = file.lastIndexOf('.');
					final String dstImage = file.substring(0, lastDot)
							+ "_tmp.jpg";
					try {
						// create command

						ProcessTask pt = cmd.getProcessTask(op, file, dstImage);

						exec.execute(pt);

						System.out.println("convert : " + "pos:" + i + "  "
								+ file + " "
								+ (time1 + System.currentTimeMillis()) + "ms"
								+ " to: " + dstImage);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IM4JavaException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					i++;
				}
			}
		}
		exec.shutdown();
		while (listener.isRunning()) {
			/*
			 * try { Thread.sleep(1000); } catch (InterruptedException e) { //
			 * TODO Auto-generated catch block e.printStackTrace(); }
			 * System.out.println("sleep");
			 */
		}

		System.out.println("Test load image loadByCMD: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	protected static class ProccessTerminationListener implements
			ProcessEventListener {
		private static final List<String> pid = new CopyOnWriteArrayList<String>();

		// empty implementation
		public void processInitiated(ProcessEvent pEvent) {
				if(pid.isEmpty()) {
					System.out.println("message to consumer, process has started");
				}
				pid.add(pEvent.toString());
			
		}

		// save the started process
		public void processStarted(ProcessEvent pEvent) {

		}

		// print return-code or stack-trace
		public void processTerminated(ProcessEvent pEvent) { //
				pid.remove(pEvent.toString());
				if(pid.isEmpty()) {
					System.out.println("message to consumer, process has ended");
				}

		}

		// check if thread is still running
		public boolean isRunning() {
			return !pid.isEmpty();
		}

	}

	private static void loadByCMD() {
		final long time = -System.currentTimeMillis();
		final File dir = new File(
				"/Users/amo/Pictures/Export/Portugal_41mm/Boom/Boom_Flickr");
		// create command
		final ConvertCmd cmd = new ConvertCmd();

		// create the operation, add images and operators/options
		final IMOperation op = new IMOperation();
		// op.resize(88);
		op.size(160);
		op.addImage();
		op.thumbnail(160);
		op.addImage(); // placeholder output filename
		if (dir.isDirectory()) {
			final File[] files = dir.listFiles();
			int i = 0;
			for (final File f : files) {
				final long time1 = -System.currentTimeMillis();
				String file = f.getAbsolutePath();
				if (file.contains(".jpg")) {
					final int lastDot = file.lastIndexOf('.');
					final String dstImage = file.substring(0, lastDot)
							+ "_tmp.jpg";
					try {
						cmd.run(op, file, dstImage);
						System.out.println("convert : " + "pos:" + i + "  "
								+ file + " "
								+ (time1 + System.currentTimeMillis()) + "ms"
								+ " to: " + dstImage);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IM4JavaException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					i++;
				}
			}
		}
		System.out.println("Test load image loadByCMD: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	private final class AsyncEventListener implements ProcessEventListener {

		@Override
		public void processInitiated(ProcessEvent pEvent) {
			// TODO Auto-generated method stub

		}

		@Override
		public void processStarted(ProcessEvent pEvent) {
			// TODO Auto-generated method stub

		}

		@Override
		public void processTerminated(ProcessEvent pEvent) {
			// TODO Auto-generated method stub

		}

	}

	private static void loadByToolkit() {
		final long time = -System.currentTimeMillis();
		final File dir = new File("/Users/amo/Pictures/Export/Portugal_41mm");
		if (dir.isDirectory()) {
			final File[] files = dir.listFiles();
			for (final File f : files) {
				if (f.getAbsolutePath().contains(".jpg")) {
					final Image img = Toolkit.getDefaultToolkit().createImage(
							f.getAbsolutePath());

					final BufferedImage buff = toBufferedImageScale(img, 800,
							600);
				}
			}
		}

		System.out.println("Test load image loadByToolkit: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	private static void loadByToolkitModified() {
		final long time = -System.currentTimeMillis();
		final Image img = getImage(Image.class,
				"/home/amo/Bilder/Picasa Exports/Joern/P1040420.jpg");
		final Image tmp = img.getScaledInstance(800, 600, 0);
		final BufferedImage buff = toBufferedImage(tmp);
		System.out.println("Test load image loadByToolkitModified: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	public static Image getImage(final Class relativeClass,
			final String filename) {
		Image returnValue = null;
		final InputStream is = relativeClass.getResourceAsStream(filename);
		if (is != null) {
			final BufferedInputStream bis = new BufferedInputStream(is);
			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
			try {
				int ch;
				while ((ch = bis.read()) != -1) {
					baos.write(ch);
				}
				returnValue = Toolkit.getDefaultToolkit().createImage(
						baos.toByteArray());
			} catch (final IOException exception) {
				System.err.println("Error loading: " + filename);
			}
		}
		return returnValue;
	}

	private static void loadStandard() {
		final long time = -System.currentTimeMillis();
		final File dir = new File("/Users/amo/Pictures/Export/Portugal_41mm");
		if (dir.isDirectory()) {
			final File[] files = dir.listFiles();
			for (final File f : files) {
				if (f.getAbsolutePath().contains(".jpg")) {
					final BufferedImage buff = toBufferedImageScale(
							getRawFile(f.getAbsolutePath()), 800, 600);
				}
			}
		}

		System.out.println("Test load image standard: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	private static void loadStandard2() {
		final long time = -System.currentTimeMillis();
		final File dir = new File("/Users/amo/Pictures/Export/Portugal_41mm");
		if (dir.isDirectory()) {
			final File[] files = dir.listFiles();
			for (final File f : files) {
				if (f.getAbsolutePath().contains(".jpg")) {

					BufferedImage buff = getRawFile(f.getAbsolutePath());
					buff = resize(buff, 800, 600);
				}
			}
		}

		System.out.println("Test load image standard: "
				+ (time + System.currentTimeMillis()) + "ms");
		System.out.println("Total Memory" + Runtime.getRuntime().totalMemory());
		System.out.println("Free Memory" + Runtime.getRuntime().freeMemory());
	}

	private static BufferedImage getRawFile(final String imageName) {

		BufferedImage myPicture = null;
		try {
			// myPicture = decoder.decodeAsBufferedImage();
			myPicture = ImageIO.read(new File(imageName));
		} catch (final ImageFormatException e) {
			e.printStackTrace();
		} catch (final IOException e) {
			e.printStackTrace();
		} catch (final Exception e) {

		}
		return myPicture;
	}

	private static BufferedImage resize(BufferedImage myPicture,
			final int width, final int hight) {
		// 88 vs 66 -> 4:3
		if (myPicture.getWidth() > width && myPicture.getHeight() > hight) {
			// check landscape or portrait
			final double direction = (double) myPicture.getWidth()
					/ (double) myPicture.getHeight();
			if (direction > 1) {
				myPicture = GraphicsUtilities.createThumbnail(myPicture, width,
						(int) (width / direction));
			} else if (direction < 1) {
				myPicture = GraphicsUtilities.createThumbnail(myPicture,
						(int) (width * direction), width);
			} else {
				myPicture = GraphicsUtilities.createThumbnail(myPicture, width,
						width);
			}

		}
		return myPicture;
	}

	// This method returns a buffered image with the contents of an image
	public static BufferedImage toBufferedImage(Image image) {
		if (image instanceof BufferedImage) {
			return (BufferedImage) image;
		}

		// This code ensures that all the pixels in the image are loaded
		image = new ImageIcon(image).getImage();

		// Determine if the image has transparent pixels; for this method's
		// implementation, see Determining If an Image Has Transparent Pixels

		// Create a buffered image with a format that's compatible with the
		// screen
		final BufferedImage bimage = new BufferedImage(image.getWidth(null),
				image.getHeight(null), BufferedImage.TYPE_INT_RGB);

		// Copy image to buffered image
		final Graphics g = bimage.createGraphics();

		// Paint the image onto the buffered image
		g.drawImage(image, 0, 0, null);
		g.dispose();

		return bimage;
	}

	public static BufferedImage toBufferedImageScale(Image image,
			final int width, final int hight) {

		// This code ensures that all the pixels in the image are loaded
		image = new ImageIcon(image).getImage();
		// Create a buffered image with a format that's compatible with the
		// screen
		BufferedImage bimage = null;

		/*
		 * bimage = new BufferedImage(image.getWidth(null),
		 * image.getHeight(null), BufferedImage.TYPE_INT_RGB); // Copy image to
		 * buffered image Graphics g = bimage.createGraphics();
		 * ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
		 * RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		 */

		if (image.getWidth(null) > width && image.getHeight(null) > hight) {
			// check landscape or portrait
			final double direction = (double) image.getWidth(null)
					/ (double) image.getHeight(null);
			if (direction > 1) {
				bimage = getConfiguration().createCompatibleImage(width,
						(int) (width / direction));

				// Copy image to buffered image
				final Graphics g = bimage.createGraphics();
				((Graphics2D) g).setRenderingHint(
						RenderingHints.KEY_INTERPOLATION,
						RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				// Paint the image onto the buffered image
				g
						.drawImage(image, 0, 0, width,
								(int) (width / direction), null);
				g.dispose();
			} else if (direction < 1) {

				bimage = getConfiguration().createCompatibleImage(
						(int) (width * direction), width);
				// Copy image to buffered image
				final Graphics g = bimage.createGraphics();
				((Graphics2D) g).setRenderingHint(
						RenderingHints.KEY_INTERPOLATION,
						RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				// Paint the image onto the buffered image
				g
						.drawImage(image, 0, 0, (int) (width * direction),
								width, null);
				g.dispose();
			} else {

				bimage = getConfiguration().createCompatibleImage(width, width);
				// Copy image to buffered image
				final Graphics g = bimage.createGraphics();
				((Graphics2D) g).setRenderingHint(
						RenderingHints.KEY_INTERPOLATION,
						RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				g.drawImage(image, 0, 0, width, width, null);
				g.dispose();
			}

		}

		return bimage;
	}

	private static GraphicsConfiguration getConfiguration() {
		return GraphicsEnvironment.getLocalGraphicsEnvironment()
				.getDefaultScreenDevice().getDefaultConfiguration();
	}

}
